﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.ServiceModel.Syndication;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using Douban.Api.Authorization;
using Douban.Api.Infrastructure;
using Douban.Api.Templates;
using Douban.Api.Web;
using Lipcas.Common.Extensions;

namespace Douban.Api.Mail
{
    /// <summary>
    /// Click here <see cref="http://www.douban.com/service/apidoc/reference/doumail"/> to see mail api details
    /// </summary>
    public class MailServiceProvider
    {
        private const string InboxUrl = "http://api.douban.com/doumail/inbox";
        private const string OutboxUrl = "http://api.douban.com/doumail/outbox";
        private const string SendMailUrl = "http://api.douban.com/doumails";
        private const string MailUrl = "http://api.douban.com/doumail/";
        private const string BatchDeleteMailUrl = "http://api.douban.com/doumail/delete";

        public DoubanContext Context { get; set; }

        /// <summary>
        /// Get Inbox or Outbox based on mailboxType
        /// </summary>
        /// <param name="mailboxType"></param>
        /// <param name="unread">
        ///     if unread is true, return only unread messages, unread is not working 
        ///     if mailboxType is MailboxType.Outbox
        /// </param>
        /// <returns></returns>
        public Mailbox GetMailbox(MailboxType mailboxType, bool unread = false)
        {
            return GetMailbox(mailboxType, 1, 50, unread);
        }

        /// <summary>
        /// Get Inbox or Outbox based on mailboxType
        /// </summary>
        /// <param name="mailboxType"></param>
        /// <param name="unread">
        ///     if unread is true, return only unread messages, unread is not working 
        ///     if mailboxType is MailboxType.Outbox
        /// </param>
        /// <param name="startIndex">start index for the first mail</param>
        /// <param name="maxResults"></param>
        /// <returns></returns>
        public Mailbox GetMailbox(MailboxType mailboxType, int startIndex, int maxResults, bool unread = false)
        {
            return Context.GetProtectedResource<Mailbox>(
                GetMailboxUrl(mailboxType, unread), startIndex, maxResults);
        }

        /// <summary>
        /// Mark a mail as read
        /// </summary>
        /// <param name="mailId"></param>
        /// <returns></returns>
        public bool MarkMailRead(string mailId)
        {
            Template template = TemplateLoader.Instance.Load("Mail.MarkMail");

            AuthorizedResponse response = Context.SendResource(
                mailId, template.ToString(), HttpMethods.Put);

            return response.StatusCode == HttpStatusCode.Created;
        }

        /// <summary>
        /// Batch mark mails as read
        /// </summary>
        /// <param name="mailIds"></param>
        /// <returns></returns>
        public bool BatchMarkMailRead(IEnumerable<string> mailIds)
        {
            Template entriesTemplate = TemplateLoader.Instance.Load("Common.Entries");
            List<Template> templates = new List<Template>();

            mailIds.ForEach(id =>
            {
                Template template = TemplateLoader.Instance.Load("MarkMailEntry");
                template.Replace("id", id);

                templates.Add(template);
            });

            entriesTemplate.BatchReplace(templates);

            AuthorizedResponse response = Context.SendResource(
                MailUrl, entriesTemplate.ToString(), HttpMethods.Put);

            return response.StatusCode == HttpStatusCode.Accepted;
        }


        /// <summary>
        /// Batch delete mails
        /// </summary>
        /// <param name="mailIds"></param>
        /// <returns></returns>
        public bool BatchDeleteMail(IEnumerable<string> mailIds)
        {
            Template entriesTemplate = TemplateLoader.Instance.Load("Common.Entries");
            List<Template> templates = new List<Template>();

            mailIds.ForEach(id =>
            {
                Template template = TemplateLoader.Instance.Load("DeleteMailEntry");
                template.Replace("id", id);

                templates.Add(template);
            });

            entriesTemplate.BatchReplace(templates);

            AuthorizedResponse response = Context.SendResource(
                BatchDeleteMailUrl, entriesTemplate.ToString(), HttpMethods.Post);

            return response.StatusCode == HttpStatusCode.OK;
        }


        /// <summary>
        /// Delete a mail
        /// </summary>
        /// <param name="mailId"></param>
        /// <returns></returns>
        public bool DeleteMail(string mailId)
        {
            AuthorizedResponse response = Context.SendResource(
                mailId, "", HttpMethods.Delete);

            return response.StatusCode == HttpStatusCode.OK;
        }

        /// <summary>
        /// Get a mail
        /// </summary>
        /// <param name="mailId"></param>
        /// <param name="keepRead">if keepRead is true, the mail will stay unread</param>
        /// <returns></returns>
        public Message GetMail(string mailId, bool keepRead = false)
        {
            return Context.GetProtectedResource<Message>(mailId + "?keep-unread=" + keepRead);
        }

        /// <summary>
        /// Send a mail
        /// </summary>
        /// <param name="receiverId">the receiver id</param>
        /// <param name="title"></param>
        /// <param name="content"></param>
        /// <param name="captchaToken"></param>
        /// <param name="captchaUrl"></param>
        /// <returns>
        ///     if sending mail frequently, this will return false
        /// </returns>
        public bool TrySendMail(string receiverId, string title, string content, 
            out string captchaToken, out string captchaUrl)
        {
            captchaToken = string.Empty;
            captchaUrl = string.Empty;

            Template template = TemplateLoader.Instance.Load("Mail.SendMail");

            template
                .Replace("receiver_id", receiverId)
                .Replace("title", title)
                .Replace("content", content);

            try
            {
                AuthorizedResponse response = Context.SendResource(
                    SendMailUrl, template.ToString(), HttpMethods.Post);

                if (response.StatusCode == HttpStatusCode.Created)
                {
                    response.Close();
                    return true;
                }
            } 
            catch (WebException ex)
            {
                if (ex.Message.Contains("403") && ex.Status == WebExceptionStatus.ProtocolError)
                {
                    StreamReader reader = new StreamReader(ex.Response.GetResponseStream());
                    string captcha = HttpUtility.HtmlDecode(reader.ReadToEnd());

                    QueryParameterCollection collection = QueryParameterCollection.Resolve(captcha);
                    captchaToken = collection.Get("captcha_token");
                    captchaUrl = collection.Get("captcha_url");

                    reader.Close();
                    ex.Response.Close();
                }
            }


            return false;
        }

        public bool SendMail(
            string receiverId, string title, string content, 
            string captchaToken, string captchaString)
        {
            Template template = TemplateLoader.Instance.Load("Mail.SendMail_Captcha");

            template
                .Replace("receiver_id", receiverId)
                .Replace("title", title)
                .Replace("content", content)
                .Replace("captcha_token", captchaToken)
                .Replace("captcha_string", captchaString);

            AuthorizedResponse response = Context.SendResource(
                SendMailUrl, template.ToString(), HttpMethods.Post);

            return response.StatusCode == HttpStatusCode.Created;
        }      

        private string GetMailboxUrl(MailboxType boxType, bool unread)
        {
            return boxType == MailboxType.Inbox ? 
                (unread ? InboxUrl + "/unread" : InboxUrl) : 
                OutboxUrl;
        }
    }
}
